﻿namespace KinematicCharacterController
{
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.SceneManagement;

    /// <summary>
    /// 增加扩展支持多Scene局部物理效果
    /// </summary>
    public static class DennyGuoExtend
    {
        public struct SceneKinematicCharacterSystemInfo
        {
            public Scene scene;
            public KinematicCharacterSystem System;
        }

        private static Dictionary<int, SceneKinematicCharacterSystemInfo> s_AllSystemInfo = new Dictionary<int, SceneKinematicCharacterSystemInfo>();
        private static bool s_PhysicsEnable = true;

        static DennyGuoExtend()
        {
            Physics.autoSimulation = false;
            Physics2D.simulationMode = SimulationMode2D.Script;

            SceneManager.sceneUnloaded += (scene) =>
                 {
                     if (s_AllSystemInfo.ContainsKey(scene.handle))
                     {
                         s_AllSystemInfo.Remove(scene.handle);
                     }
                 };
        }       

        public static void EnsureCreation(Scene scene)
        {
            if (scene.handle != 0 && !s_AllSystemInfo.ContainsKey(scene.handle))
            {
                bool needResetScene = false;
                Scene srcScene = default;
                if (SceneManager.GetActiveScene() != scene)
                {
                    needResetScene = true;
                    srcScene = SceneManager.GetActiveScene();
                    SceneManager.SetActiveScene(scene);
                }
                

                SceneKinematicCharacterSystemInfo systemInfo = new SceneKinematicCharacterSystemInfo();
                systemInfo.scene = scene; 
                GameObject systemGameObject = new GameObject("KinematicCharacterSystem");
                systemInfo.System = systemGameObject.AddComponent<KinematicCharacterSystem>();
              
                systemGameObject.hideFlags = HideFlags.NotEditable;
                systemInfo.System.hideFlags = HideFlags.NotEditable;
                
                systemInfo.System.Settings = ScriptableObject.CreateInstance<KCCSettings>();                 
                
                s_AllSystemInfo[scene.handle] = systemInfo;

                if(needResetScene)
                {
                    SceneManager.SetActiveScene(srcScene);
                }
            }
        }

        public static void RegisterCharacterMotor(KinematicCharacterMotor motor)
        {
            KinematicCharacterSystem system = GetSystem(motor.gameObject);
            if(system != null)
            {
                system.RegisterCharacterMotor(motor);
            }
        }

        public static void UnregisterCharacterMotor(KinematicCharacterMotor motor)
        {
            KinematicCharacterSystem system = GetSystem(motor.gameObject);
            if (system != null)
            {
                system.UnregisterCharacterMotor(motor);
            }
        }

        public static void RegisterPhysicsMover(PhysicsMover mover)
        {
            KinematicCharacterSystem system = GetSystem(mover.gameObject);
            if (system != null)
            {
                system.RegisterPhysicsMover(mover);
            }
        }

        public static void UnregisterPhysicsMover(PhysicsMover mover)
        {
            KinematicCharacterSystem system = GetSystem(mover.gameObject);
            if (system != null)
            {
                system.UnregisterPhysicsMover(mover);
            }
        }

        public static void Simulate(float deltaTime)
        {
            foreach (var item in s_AllSystemInfo)
            {
                if (s_PhysicsEnable)
                {
                    item.Value.scene.GetPhysicsScene().Simulate(deltaTime);
                    item.Value.scene.GetPhysicsScene2D().Simulate(deltaTime);
                }

                if (item.Value.System.Settings.Interpolate)
                {
                    item.Value.System.PreSimulationInterpolationUpdate(deltaTime);
                }

                item.Value.System.Simulate(deltaTime, item.Value.System.CharacterMotors, item.Value.System.PhysicsMovers);


                if (item.Value.System.Settings.Interpolate)
                {
                    item.Value.System.PostSimulationInterpolationUpdate(deltaTime);
                }
            }
        }

        public static KinematicCharacterSystem GetSystem(GameObject go)
        {
            Scene scene = go.scene;             
            return GetSystem(scene);
        }

        public static KinematicCharacterSystem GetSystem(Scene scene)
        {             
            SceneKinematicCharacterSystemInfo result;
            if (s_AllSystemInfo.TryGetValue(scene.handle, out result))
            {
                return result.System;
            }
            return null;
        }

        public static void SetInterpolateEnable(bool value)
        {
            foreach(var item in s_AllSystemInfo)
            {               
                if (item.Value.System != null)
                {
                    item.Value.System.Settings.Interpolate = value;
                }
            }
        }

        public static void SetPhysicsEnable(bool value)
        {
            s_PhysicsEnable = value;
        } 
    }
}
